#include <iostream>
#include <sstream>
#include <map>
#include <unistd.h>
#include "opencv2/opencv.hpp"
#include "opencv2/xfeatures2d.hpp"


#include <math.h>
#include <time.h>

using namespace cv;
using namespace std;

bool debug = false;
string configPath = "/usr/local/share/OpenCV/haarcascades/haarcascade_frontalface_alt.xml";
clock_t start;
clock_t clt;

//文字水印参数
// string watermark_text_color = "#ffffff";
// int watermark_text_color_red = 255;
// int watermark_text_color_green = 255;
// int watermark_text_color_blue = 255;
// int watermark_text_font = 0;  //参考core_c.h #define CV_FONT_HERSHEY_{XXXXXX}
// int watermark_text_x = 0;
// int watermark_text_y = 0;
// int watermark_text_thickness = 1;
// double watermark_text_scale = 0.8f;


struct WATERMARK{
    string text;
    string color = "#ffffff";
    int red = 255;
    int green = 255;
    int blue = 255;
    int font = 0;
    int x = 0;
    int y = 0;
    int thickness = 1;
    double scale = 0.8f;
};


#define show_debug(tip,message) if(debug){ cout << tip << message << endl;}
#define TCLIP_VERSION "2.200609"

int detectFace(Mat img, string faceCascadeName)
{
    CascadeClassifier faceCascade;
    //rect对象用来存储一个矩形框的左上角坐标、宽度和高度。
    std::vector<Rect> faces;

    Mat imgGray;
    int faceSize;
    int Y;

    start = clock();
    
    if( !faceCascade.load(faceCascadeName)){
        printf("[error] can not load classifier file ! [use -H for help]\n");
        return -1; 
    }

    clt = clock() - start;
    show_debug("read face config cost time ", (double)clt/CLOCKS_PER_SEC);
    //二值图像
    cvtColor(img, imgGray, COLOR_BGR2GRAY);
    //直方图均衡化：是一种提高图像对比度的方法，拉伸图像灰度值范围。
    equalizeHist(imgGray, imgGray);
    //把检测到的人脸等用矩形（或者圆形等其他图形）画出来
    faceCascade.detectMultiScale( imgGray, faces, 1.1, 2, 0|CASCADE_SCALE_IMAGE, Size(30, 30) );
    faceSize = faces.size();
    show_debug("detectFace::face size is ", faceSize);

    if( faceSize>0){
        show_debug("detectFace.faces[0].y is ", faces[0].y);
        show_debug("detectFace.faces[0].height  is ", faces[0].height );
        show_debug("detectFace.faces[0].width  is ", faces[0].width );
        show_debug("detectFace.faces[faceSize -1].y is ", faces[faceSize -1].y);
        show_debug("detectFace.faces[faceSize -1].height is ", faces[faceSize -1].height);
        show_debug("detectFace.faces[faceSize -1].width is ", faces[faceSize -1].width);
        Y = faces[faceSize-1].y - faces[faceSize-1].height/2;
        if( Y>img.size().height/2){
            return -1;
        }
        return Y<0?0:Y;
     }
    return -1;
}

int detectCharacter(Mat img)
{
    //特征点X坐标开始位置 
    int startX = 0;
    //特征点X坐标结束位置
    int endX = 0;
    //Y坐标段数字索引
    int sectionIndex = 0;
    //每个Y坐标段中特征点的数量
    map<int, int> sectionNum;
    //总共特征点数量
    int total = 0;
    //每个Y坐标段的平均特征点数量
    int avg = 0;
    //需要连续的阀值
    int conNum = 4;
    //
    int flag = 0;
    //
    int counter = 0;
    //
    int Y = 0;
    //指针
    vector<KeyPoint> keypoints;
    //定义一个SurfFeatureDetector（SURF） 特征检测类对象
    Ptr<xfeatures2d::SURF> detector = cv::xfeatures2d::SURF::create();

    if( detector.empty()){
        printf("Can not create detector or descriptor exstractor or descriptor matcher of given types\n");
        return -1;
    }

    startX = 0;
    endX = img.size().width;

    detector->detect(img, keypoints);
    for( vector<KeyPoint>::iterator i = keypoints.begin(); i != keypoints.end(); i++){
        if( i->pt.x>startX  && i->pt.x<endX ) {
            sectionIndex = (int)ceil(i->pt.y/10);
            sectionNum[sectionIndex] = sectionNum[sectionIndex]+1;
            total = total+1;
        }
    }

    if( sectionNum.size()==0){
        return -1;
    }

    avg = total/sectionNum.size();
    for( map<int, int>::iterator i = sectionNum.begin(); i != sectionNum.end(); i++ ){
        show_debug(i->first, i->second);
    }
    
    show_debug("detectCharacter::avg is ", avg);

    //检测特征点分布是否均匀
    int sliceTotal = 10;
    int sliceNum = sectionNum.size() / sliceTotal;
    int sliceCounter = 0;

    for( int m=0; m<sliceTotal; m++){
        for ( int n = m* sliceNum; n<(m+1)*sliceNum; n++){
            if( sectionNum[n]>=avg){
                sliceCounter++;
                break;
            }
        }
    }

    if(sliceCounter >= sliceTotal){
        return -1;
    }
    //检测特征点主要分布区域[找最开始连续大于avg的Y]
    for( map<int, int>::iterator i = sectionNum.begin(); i != sectionNum.end(); i++){
        if(i->second >= avg && flag == 0){
            counter++;
        }else{
            counter = 0;
        }

        if(counter >= conNum && flag==0){
            Y = i->first;
            flag = 1;
        }
    }

    if( Y > conNum && Y<img.size().height/4){
        return (Y - conNum - 11) * sliceTotal < 0 ? 0 : (Y-conNum-11)*sliceTotal;
    }else if( Y>conNum ){
        return ( Y-conNum )* sliceTotal;
    }
    return Y*10;

}

void show_help()
{
    printf("Usage: tclip [options] [-s] <source_file> [--] [args...]\n");
    printf("-s<path>   the path of source file\n");
    printf("-d<path>   the path of destination file\n");
    printf("-w<int>    the width of destination file. default value is 300\n");
    printf("-h<int>    the height of destination file. default value is 180\n");
    printf("-c<path>   the path of config file.\n");
	printf("           default path is /usr/local/share/OpenCV/haarcascades/haarcascade_frontalface_alt.xml\n");
    printf("-t<string> the watermark text.\n");
    printf("-C<string> the watermark text color default value is #ffffff.\n");
    printf("-F<int>    the watermark text font(0-7) default value is 0.\n");
    printf("-X<int>    the watermark text position x default value is 20.\n");
    printf("-Y<int>    the watermark text position y default value is height - 20.\n");
    printf("-T<int>    the watermark text thinkness default value is 1.\n");
    printf("-S<int>    the watermark text scale default value is 0.8.\n");
    printf("-m         open debug model\n");
    printf("-v         output version information and exit\n");
}

void convert_color_hex2rgb(const char *colorStr, int* r, int* g, int* b) 
{
	std::string coloRaw(colorStr);

	if (coloRaw.at(0) == '#') {
		coloRaw = coloRaw.erase(0, 1);
	}

	std::istringstream(coloRaw.substr(0,2)) >> std::hex >> *r;
	std::istringstream(coloRaw.substr(2,2)) >> std::hex >> *g;
	std::istringstream(coloRaw.substr(4,2)) >> std::hex >> *b;
}

int reset_image(string sourcePath, int destWidth, int destHeight, string destPath, WATERMARK watermark)
{
    Mat image, destImage;
    float ratioWidth = 0,  
          ratioHeight = 0, 
          ratio = 0;
    int clipTop = 0,
        clipBottom = 0,
	    clipLeft = 0,
	    clipRight = 0;
    int result = 0;
    Size tmpSize;

    //文字水印参数
	string watermark_text_color = watermark.color;
	int watermark_text_color_red = watermark.red;
	int watermark_text_color_green = watermark.green;
	int watermark_text_color_blue = watermark.blue;
	int watermark_text_font = watermark.font;  //参考core_c.h #define CV_FONT_HERSHEY_{XXXXXX}
	int watermark_text_x = watermark.x;
	int watermark_text_y = watermark.y;
	int watermark_text_thickness = watermark.thickness;
	double watermark_text_scale = watermark.scale;

    

    image = imread(sourcePath);
    if (!image.data){
        printf("[error] do not load pic \n");
        return 0;
    }
    //宽度的3倍小于高度
    if( image.size().width * 3 <= image.size().height ){
        ratio = (float) destWidth/image.size().width;
        tmpSize = Size((int)(image.size().width* ratio), (int)(image.size().height * ratio));
        destImage = Mat(tmpSize, CV_32S);
        resize(image, destImage, tmpSize);
        clipTop = 0;
        clipBottom = destHeight - destImage.size().height;
        clipLeft = 0;
        clipRight = 0;
        destImage.adjustROI(clipTop, clipBottom, clipLeft, clipRight);
        imwrite(destPath, destImage);
        return 1;
    }
    //
    ratio = (float)300.0 / image.size().width;
    tmpSize = Size((int)(image.size().width* ratio), (int)(image.size().height * ratio));
    destImage = Mat(tmpSize, CV_32S);
    resize(image, destImage, tmpSize);
    result = detectFace( destImage, configPath);
    if(result == -1){
        show_debug("start to detectCahracter ", "");
        result = detectCharacter( destImage );
    }
    result = result == -1 ? -1 : (int)((float)result / ratio);
    ratioWidth = (float)destWidth / image.size().width;
	ratioHeight = (float)destHeight / image.size().height;
    if (ratioWidth > ratioHeight) {
		ratio = ratioWidth;
	} else {
		ratio = ratioHeight;
	}
    result = result == -1 ? -1 : (int)((float)result * ratio);
    tmpSize = Size((int)(image.size().width* ratio), (int)(image.size().height * ratio));
    destImage = Mat(tmpSize, CV_32S);
    resize(image, destImage, tmpSize);

    //原图片 宽度小于高度
    if (ratioWidth > ratioHeight) {
		if (result == -1) {
			clipTop = -((destImage.size().height - destHeight) / 2);
			clipBottom = clipTop;
		} else {
			if (destImage.size().height - result >= destHeight) {
				clipTop = -result;
				clipBottom = -(destImage.size().height - result - destHeight);
			} else {
				clipTop = -(destImage.size().height - destHeight);
			}
		}
	} else {
		clipLeft = -((destImage.size().width - destWidth) / 2);
		clipRight = clipLeft;
	}
    //Mat& Mat::adjustROI(int dtop, int dbottom, int dleft, int dright)
    destImage.adjustROI(clipTop, clipBottom, clipLeft, clipRight); 
    string waterMarkTest = watermark.text;
    show_debug("watermark text ", waterMarkTest);
    show_debug("watermark color ", watermark_text_color_red);

    if (waterMarkTest != "") {
	    putText(destImage, waterMarkTest , Point(watermark_text_x, watermark_text_y), watermark_text_font, watermark_text_scale, CV_RGB(watermark_text_color_red,watermark_text_color_green,watermark_text_color_blue), watermark_text_thickness);
	}
    try {
		imwrite(destPath, destImage);
	} catch (exception &e) {
		cerr << e.what() << endl;
        return 0;
	}
    return 1;
}

int main(int argc, char** argv)
{
    if(argc ==1 ){
        show_help();
        return 1;
    }
    string sourcePath, 
           destPath,
           configPath;
    int param;
    int destWidth, destHeight;
    WATERMARK watermark;



    while ((param = getopt(argc, argv, "vHms:d:c:w:h:t:C:F:X:Y:T:S:")) != -1 ) {
        if ( param == 's' ) {
			sourcePath = optarg;
		}else if ( param == 'd' ) {
			destPath = optarg;
		}else if ( param == 'm' ) {
			debug = true;
		}else if ( param == 'c' ) {
			configPath = optarg;
		}else if ( param == 'w' ) {
			sscanf (optarg, "%d", &destWidth);
		}else if ( param == 'h' ) {
			sscanf (optarg, "%d", &destHeight);
		}else if ( param == 't' ) {
			watermark.text = optarg;
		}else if ( param == 'C' ) {
			watermark.color = optarg;
			convert_color_hex2rgb(watermark.color.c_str(), &watermark.red, &watermark.green, &watermark.blue);
		}else if ( param == 'F' ) {
			sscanf (optarg, "%d", &watermark.font);
			if (watermark.font < 0 || watermark.font > 7) {
				watermark.font = 0;
			}
		}else if ( param == 'X' ) {
			sscanf (optarg, "%d", &watermark.x);
		}else if ( param == 'Y' ) {
			sscanf (optarg, "%d", &watermark.y);
		}else if ( param == 'T' ) {
			sscanf (optarg, "%d", &watermark.thickness);
		}else if ( param == 'S' ) {
			sscanf (optarg, "%lf", &watermark.scale);
		}else if( param == 'v'){
            cout <<  "Tclip version : " << TCLIP_VERSION << endl;
            return 0;
        }else if ( param == 'H') {
			show_help();
			return 0;
		}
    }

    reset_image(sourcePath, destWidth, destHeight, destPath, watermark);

    
    return 1;
}


